/*
 * BoardInfos.hpp
 * Author: Elias Capito, Yanick Candaux
 * PCM
 */


#ifndef BOARD_HPP_
#define BOARD_HPP_

#include "def.hpp"
#include "HolePosition.hpp"
#include "HoleJump.hpp"
#include "HolePermutation.hpp"
#include <iostream>
#include <vector>

using namespace std;

namespace pcm {

enum HolePermutationEnum{
	EReverse = 0,
	ECW60 = 1,
	ECCW60 = 2,
	EReverseCW60 = 3,
	EReverseCCW60 = 4
};

class BoardInfos {
public:

	static LL cons;

	BoardInfos();

	/**
	 * Copy constructor
	 * @param _boardInfos
	 */
	inline BoardInfos(const BoardInfos* _boardInfos){
#ifdef CACLUTATE_CONS_AND_DES
		#pragma omp atomic
		cons++;
#endif
		m_board = _boardInfos->m_board;
		m_numberJoin = _boardInfos->m_numberJoin;
		m_numberPeg = _boardInfos->m_numberPeg;
	}

	/**
	 * Destructor
	 */
	virtual ~BoardInfos();
	/**
	 * Show a board
	 * @param _board
	 * @param _size
	 * @param _name
	 */
	static void printBoard(LL _board,short int _size, string _name="") ;
	/**
	 * Update game infos for a size
	 * @param gameSize
	 */
	static void updateGameInfos(short int gameSize);
	/**
	 * Return actual game size
	 * @return game size
	 */
	inline static const short int& gameSize() {
		return m_gameSize;
	}
	/**
	 * Return actule game number hole
	 * @return game number hole
	 */
	inline static const short int& gameNumberHole() {
		return m_gameNumberHole;
	}
	/**
	 * Return holeposition for a hole
	 * @param iHole index of the hole
	 * @return hole position (lign, col)
	 */
	inline static const HolePosition& holePosition(short int iHole) {
		return m_holePosition[iHole];
	}
	/**
	 * Return jumps who can be do for hole
	 * @param iHole index of the hole
	 * @return list of jumps for hole
	 */
	inline static const vector<HoleJump>& startHoleJumps(short int iHole) {
		return m_startHoleJumps[iHole];
	}
	/**
	 * Return number jumps who can be do for hole
	 * @param iHole index of the hole
	 * @return number jumps
	 */
	inline static const size_t& startHoleJumpsSize(short int iHole){
		return m_startHoleJumpsSize[iHole];
	}
	/**
	 * Return jumps who can be to hole
	 * @param iHole index of the hole
	 * @return list of jumps to hole
	 */
	inline static const vector<HoleJump>& endlHoleJumps(short int iHole) {
		return m_endHoleJumps[iHole];
	}
	/**
	 * Return number jumps who can be to hole
	 * @param iHole index of the hole
	 * @return number jumps
	 */
	inline static const size_t& endHoleJumpsSize(short int iHole){
		return m_endHoleJumpsSize[iHole];
	}
	/**
	 * Get hole
	 * @param _boardToGet
	 * @param _holeToGet
	 * @return Has a peg or not
	 */
	inline static bool hole(LL _boardToGet,short int _holeToGet) {
		return (_boardToGet>>_holeToGet)&1;
	}
	/**
	 * Get hole
	 * @param _boardToGet
	 * @param _lignToGet
	 * @param _colToGet
	 * @returnHas a peg or not
	 */
	inline static bool hole(LL _boardToGet,short int _lignToGet,short int _colToGet){
		return hole(_boardToGet,HolePosition::convToHoleNumber(_lignToGet,_colToGet));
	}
	/**
	 * Set hole
	 * @param _boardToSet
	 * @param holeToSet
	 * @param setPeg
	 */
	inline static void setHole(LL &_boardToSet,short int holeToSet, bool setPeg){
		LL mask = (LL)1<<holeToSet;
		if(setPeg){
			_boardToSet |=mask ;
		}else{
			_boardToSet &= ~mask;
		}
	}
	/**
	 * Copy hole frome an other board
	 * @param _boardToCopy
	 * @param _baseBoard
	 * @param _holeTo
	 * @param _holeFrom
	 */
	inline static void copyHole(LL &_boardToCopy,LL _baseBoard, short int _holeTo,short int _holeFrom){
		setHole(_boardToCopy,_holeTo,hole(_baseBoard,_holeFrom));
	}
	/**
	 * Swap to hole for a board
	 * @param _boardToSwap
	 * @param _hole1
	 * @param _hole2
	 */
	inline static void swap(LL &_boardToSwap, short int _hole1,short int _hole2){
		bool tmpHole1 = hole(_boardToSwap,_hole1);
		setHole(_boardToSwap,_hole1,hole(_boardToSwap,_hole2));
		setHole(_boardToSwap,_hole2,tmpHole1);
	}
	/**
	 * Reverse a board
	 * @param _board
	 * @return board reversed
	 */
	static inline LL reverseAllBoard(LL _board) {
		LL tmp = _board;
		for(unsigned short int i=0;i<LLBITSDIV2;i++){
			swap(tmp,i,LONLONGBITSM1-i);
		}
		return tmp;
	}
	/**
	 * Set hole for object
	 * @param holeToSet
	 * @param setPeg
	 */
	inline void setHole(short int holeToSet, bool setPeg){
		LL mask = (LL)1<<holeToSet;
		if(setPeg){
			m_board |=mask ;
		}else{
			m_board &= ~mask;
		}
	}
	/**
	 * Get hole from object
	 * @param _holeToGet
	 * @return
	 */
	inline bool hole(short int _holeToGet) const{
		return (m_board>>_holeToGet)&1;
	}
	/**
	 * get hole from object
	 * @param _lignToGet
	 * @param _colToGet
	 * @return
	 */
	inline bool hole(short int _lignToGet,short int _colToGet) const{
		return hole(HolePosition::convToHoleNumber(_lignToGet,_colToGet));
	}
	/**
	 * Swap two hole for object
	 * @param _hole1
	 * @param _hole2
	 */
	inline void swap(short int _hole1,short int _hole2){
		LL tmpHole1 = hole(m_board,_hole1);
		setHole(m_board,_hole2);
		setHole(m_board,tmpHole1);
	}
	/**
	 * return boardPointer
	 * @return
	 */
	inline const LL* boardPointer(){
		return &m_board;
	}
	inline LL board() const{
		return m_board;
	}

	inline short int numberPeg() const {
		return m_numberPeg;
	}
	inline void decrementNumberPeg(){
		#pragma omp atomic
		m_numberPeg--;
	}
	inline LL numberJoin() const {
		return m_numberJoin;
	}
	inline void incrementNumberJoin(LL _increment=1){
		#pragma omp atomic
		m_numberJoin+=_increment;
	}

	/**
	 * Update a board with other board
	 * @param _boardInfos
	 */
	inline void updateBoardInfos(const BoardInfos& _boardInfos){
		m_board = _boardInfos.m_board;
		m_numberJoin = _boardInfos.m_numberJoin;
		m_numberPeg = _boardInfos.m_numberPeg;
	}

	/**
	 * Do rotation and symmetries to have the min value board on object
	 */
	void applyHolesPermutations();

	/**
	 * Initialize board
	 * @param holeToRemove
	 */
	void initBoard(short int holeToRemove);

	/**
	 * Fill board with all peg
	 */
	void fillBoard();

	/**
	 * Print board object
	 */
	void printBoard() const;
	/**
	 * Print calculated jumps
	 */
	void printJumps() const;
	/**
	 * Print calculated hole position
	 */
	void printHolePos() const;

protected:
	static  short int m_gameNumberHole;
	static  short int m_gameSize;
	static HolePosition *m_holePosition;
	static vector<HoleJump> *m_startHoleJumps;
	static size_t *m_startHoleJumpsSize;

	static vector<HoleJump> *m_endHoleJumps;
	static size_t *m_endHoleJumpsSize;

	static vector<HolePermutation> *m_holesPermutation;
	static size_t *m_holesPermutationSize;

	LL m_board;
	short int m_numberPeg;
	LL  m_numberJoin;

	/**
	 * Set game number hole
	 * @param gameSize
	 */
	inline static  void setGameNumberHole(short int gameSize){
		m_gameNumberHole = (gameSize+1)*(gameSize/2.0);
	}

	/**
	 * Set gamesize
	 * @param gameSize
	 */
	inline static void setGameSize(short int gameSize){
		m_gameSize = gameSize;
	}
	/**
	 * Pre calculate hole position
	 */
	static void calculateHolesPos();
	/**
	 * Pre calculate hole jumps
	 */
	static void calculateHolesJumps();
	/**
	 * Pre calculate rotation and symmetries
	 */
	static void calculateRotatesAndSymmetries();
};

} /* namespace pcm */
#endif /* BOARD_HPP_ */
